package com.swak.consistency.tcc.role.impls;

import com.swak.consistency.core.execption.ConsistencyException;
import com.swak.consistency.tcc.context.Phase;
import com.swak.consistency.tcc.context.TccContext;
import com.swak.consistency.tcc.context.TransLog;
import com.swak.consistency.tcc.context.TransParticipant;
import com.swak.consistency.tcc.role.AbstractTransRoler;

/**
 * 事务提供者
 * 
 * @author DELL
 */
public class TransProvider extends AbstractTransRoler {

	@Override
	public Object doHandle(TccContext context) {
		if (context.getPhase() == Phase.READY) {
			return this.preparePhase(context);
		} else if (context.getPhase() == Phase.CONFIRMING) {
			return this.confirmPhase(context);
		} else if (context.getPhase() == Phase.CANCELING) {
			return this.cancelPhase(context);
		}
		throw new ConsistencyException("事务提供  - 事务已经提交，不能加入到当前事务:" + context.toString());
	}

	private Object preparePhase(TccContext context) {

		// 开启全局事务
		this.openGlobalTrans(context);

		// 开启分支事务
		TransParticipant branch = this.openBranchTrans(context);

		// 执行业务代码(如果 doPrepare 执行成功，但是 postBranchTrans 执行失败 会导致不能执行取消)
		// 所有 postBranchTrans 必须要能影响 doPrepare 的事务
		Object result = this.doPrepare(context);

		// 确定分支事务
		this.postBranchTrans(context, branch);

		// 确定事务执行成功
		this.postGlobalTrans(context);

		// 返回数据
		return result;
	}

	/**
	 * 保证必须是执行了 Try 之后才能执行确认阶段，且只能执行一次
	 * 
	 * @param context
	 * @return
	 */
	protected Object confirmPhase(TccContext context) {

		// 校验是否需要执行
		if (this.checkPhase(context, Phase.CONFIRMING)) {

			// 执行提交
			this.doConfirm(context);
		}

		// 是否可以返回null
		return null;
	}

	/**
	 * 保证必须是执行了 Try 之后才能执行取消阶段，且只能执行一次
	 * 
	 * @param context
	 * @return
	 */
	protected Object cancelPhase(TccContext context) {

		// 校验是否需要执行
		if (this.checkPhase(context, Phase.CANCELING)) {

			// 执行取消
			this.doCancel(context);
		}

		// 是否可以返回null
		return null;
	}

	protected boolean checkPhase(TccContext context, Phase phase) {
		TransLog trans = this.transLogStorage.get(context.getTransId());
		if (trans != null && trans.getPhase() != null
				&& (trans.getPhase() == Phase.TRYED.getCode() || trans.getPhase() == phase.getCode())) {
			return true;
		}
		return false;
	}
}